package org.budo.graph.view.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.budo.graph.entity.DubboProvider;
import org.budo.graph.service.BudoGraphService;
import org.budo.support.lang.util.MapUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

import com.alibaba.druid.support.json.JSONUtils;

/**
 * @author limingwei
 */
public class ApplicationForceUtil {
    private static final Logger log = LoggerFactory.getLogger(ApplicationForceUtil.class);

    public static Collection<Map<String, Object>> applicationForce(ApplicationContext applicationContext) {
        Map<String, List<String>> serviceToInterfaceMap = new HashMap<String, List<String>>();
        Map<String, List<String>> referenceToInterfaceMap = new HashMap<String, List<String>>();
        Map<String, List<String>> interfaceToReferenceMap = new HashMap<String, List<String>>();

        Collection<DubboProvider> dubboProviders = DubboProviderUtil.getDubboProviders_2(applicationContext, false);
        for (DubboProvider dubboProvider : dubboProviders) {
            String applicationName = dubboProvider.getApplicationName();
            String host = dubboProvider.getHost();
            Integer port = dubboProvider.getPort();
            if (null != applicationName && !applicationName.isEmpty() //
                    && null != host && !host.isEmpty() //
                    && null != port && port > 0) {
                loadDubboService(serviceToInterfaceMap, dubboProvider);
                loadDubboReference(referenceToInterfaceMap, dubboProvider);
            }
        }

        mapInterfaceToReference(referenceToInterfaceMap, interfaceToReferenceMap);

        return forceLinkMap(serviceToInterfaceMap, interfaceToReferenceMap);
    }

    private static Collection<Map<String, Object>> forceLinkMap(Map<String, List<String>> serviceToInterfaceMap, Map<String, List<String>> interfaceToReferenceMap) {
        Map<String, Map<String, Object>> forceLinks = new HashMap<String, Map<String, Object>>();
        for (Entry<String, List<String>> entry : serviceToInterfaceMap.entrySet()) {
            String source = entry.getKey();
            for (String interfaceName : entry.getValue()) { // interface
                List<String> targets = interfaceToReferenceMap.get(interfaceName);
                if (null != targets) {
                    for (String target : targets) {
                        Map<String, Object> stringObjectMap = MapUtil.stringObjectMap("source", source, //
                                "target", target);
                        forceLinks.put(source + ">" + target, stringObjectMap);
                    }
                }
            }
        }
        return forceLinks.values();
    }

    private static void mapInterfaceToReference(Map<String, List<String>> referenceToInterfaceMap, Map<String, List<String>> interfaceToReferenceMap) {
        for (Entry<String, List<String>> entry : referenceToInterfaceMap.entrySet()) {
            String reference = entry.getKey();
            List<String> interfaces = entry.getValue();

            for (String interfaceName : interfaces) {
                List<String> references = interfaceToReferenceMap.get(interfaceName);
                if (null == references) {
                    references = new ArrayList<String>();
                    interfaceToReferenceMap.put(interfaceName, references);
                }
                references.add(reference);
            }
        }
    }

    private static void loadDubboReference(Map<String, List<String>> referenceToInterfaceMap, DubboProvider dubboProvider) {
        String applicationName = dubboProvider.getApplicationName();
        String host = dubboProvider.getHost();
        Integer port = dubboProvider.getPort();

        String reference = requestApi(host, port, "/dubbo-reference.json");
        if (reference.startsWith("[")) {
            List _list = (List) JSONUtils.parse(reference);
            for (int i = 0; i < _list.size(); i++) {
                Map _map = (Map) _list.get(i);
                Object interfaceName = _map.get("interface");

                List<String> list = referenceToInterfaceMap.get(applicationName);
                if (null == list) {
                    list = new ArrayList<String>();
                    referenceToInterfaceMap.put(applicationName, list);
                }
                list.add(interfaceName.toString());
            }
        }
    }

    private static void loadDubboService(Map<String, List<String>> serviceToInterfaceMap, DubboProvider dubboProvider) {
        String applicationName = dubboProvider.getApplicationName();
        String host = dubboProvider.getHost();
        Integer port = dubboProvider.getPort();

        String service = requestApi(host, port, "/dubbo-service.json");

        if (service.startsWith("[")) {
            List _list = (List) JSONUtils.parse(service);
            for (int i = 0; i < _list.size(); i++) {
                Map _map = (Map) _list.get(i);
                Object interfaceName = _map.get("interface");

                List<String> list = serviceToInterfaceMap.get(applicationName);
                if (null == list) {
                    list = new ArrayList<String>();
                    serviceToInterfaceMap.put(applicationName, list);
                }
                list.add(interfaceName.toString());
            }
        }
    }

    private static String requestApi(String host, Integer port, String apiUrl) {
        try {
            BudoGraphService budoGraphService2 = BudoGraphViewUtil.buildBudoGraphService(host, port, null);
            String serviceDubboDruidStat = budoGraphService2.serviceDubboDruidStat(apiUrl);
            return new String(serviceDubboDruidStat.getBytes());
        } catch (Throwable e) {
            if (DubboProviderUtil.isInvokeTimeoutError(e)) {
                log.error("#134 writeApiResponse error, e=" + e);
            } else {
                log.error("#136 writeApiResponse error, e=" + e, e);
            }
            return "";
        }
    }
}
